<?php
/**
 * 易优CMS
 * ============================================================================
 * 版权所有 2016-2028 海口快推科技有限公司，并保留所有权利。
 * 网站地址: http://www.eyoucms.com
 * ----------------------------------------------------------------------------
 * 如果商业用途务必到官方购买正版授权, 以免引起不必要的法律纠纷.
 * ============================================================================
 * Author: 陈风任 <491085389@qq.com>
 * Date: 2020-05-07
 */

namespace app\api\model\v1;

use think\Db;
use think\Cache;
use think\Config;

/**
 * 小程序商城 秒杀订单模型
 */
load_trait('controller/Jump');

class WeappSeckill extends UserBase
{
    use \traits\controller\Jump;

    private $miniproInfo = [];

    //初始化
    protected function initialize()
    {
        // 需要调用`Model`的`initialize`方法
        parent::initialize();
        $this->miniproInfo = model('ShopPublicHandle')->getSpecifyAppletsConfig();
    }

    // 判断秒杀商品库存
    public function IsSaledOut($post = [])
    {
        if (!empty($post['product_id'])) {
            if (!empty($post['spec_value_id'])) {
                $SpecWhere = [
                    'aid' => $post['product_id'],
                    'goods_id' => $post['goods_id'],
                    'spec_value_id' => $post['spec_value_id']
                ];

                $spec_stock = Db::name('weapp_seckill_product_spec_value')->where($SpecWhere)->value('seckill_stock');
                $oriSpecWhere = [
                    'aid' => $post['product_id'],
                    'spec_value_id' => $post['spec_value_id']
                ];

                $ori_spec_stock = Db::name('product_spec_value')->where($oriSpecWhere)->value('spec_stock');
                if ($spec_stock > $ori_spec_stock) $spec_stock = $ori_spec_stock;
                if (empty($spec_stock)) {
                    $this->error('商品已售罄！');
                }
                if ($spec_stock < $post['product_num']) {
                    $this->error('商品最大库存：' . $spec_stock);
                }
            } else {
                $secWhere = [
                    'aid' => $post['product_id'],
                    'goods_id' => $post['goods_id'],
                ];
                $stock_count = Db::name('weapp_seckill_archives')->where($secWhere)->value('stock');

                $ArchivesWhere = [
                    'aid'     => $post['product_id'],
                    'arcrank' => ['>=', 0]
                ];
                $ori_stock_count   = Db::name('archives')->where($ArchivesWhere)->getField('stock_count');
                if ($stock_count > $ori_stock_count) $stock_count = $ori_stock_count;
                if (empty($stock_count)) {
                    $this->error('商品已售罄！');
                }
                if ($stock_count < $post['product_num']) {
                    $this->error('商品最大库存：' . $stock_count);
                }
            }
        }
    }

    //限购
    public function LimitBuy($post = [],$users = [])
    {
        $act_id = Db::name('weapp_seckill_archives')->where('goods_id',$post['goods_id'])->value('act_id');
        $act = Db::name('weapp_seckill')->where('id',$act_id)->find();
        empty($act) && $this->error('秒杀活动不存在！');

        $where['b.users_id'] = $users['users_id'];
        $where['b.is_seckill_order'] = $act_id;
        $where['b.order_status'] = ['in',[1,2,3]];
        $today_msg = "";
        if (1 == $act['is_limit']){
            $sum = Db::name('shop_order_details')->alias('a')
                ->join('shop_order b','a.order_id = b.order_id')
                ->where($where)
                ->sum('a.num');
        }else if (2 == $act['is_limit']){
            $today = date('Y-m-d');
            $today = strtotime($today);
            $where['b.pay_time'] = ['>=',$today];
            $sum = Db::name('shop_order_details')->alias('a')
                ->join('shop_order b','a.order_id = b.order_id')
                ->where($where)
                ->sum('a.num');
            $today_msg = "每日";
        }

        if (1 == $act['is_limit'] || 2 == $act['is_limit']){
            if ($act['limit'] <= $sum){
                $this->error("商品{$today_msg}限购{$act['limit']}件,您购买已达数量！");
            }
            if($act['limit'] < ($sum + $post['product_num'])){
                $post['product_num'] = $act['limit'] - $sum;
                $this->error("商品{$today_msg}限购{$act['limit']}件,您最多还可购买{$post['product_num']}件！");
            }
        }
    }

    // 立即购买
    public function ShopBuyNow($post = [])
    {
        // 查询条件
        $ArchivesWhere = [
            'aid' => $post['product_id'],
//            'goods_id'      => $post['goods_id'],
            'arcrank' => ['>=', 0]
        ];
        $count = Db::name('archives')->where($ArchivesWhere)->count();

        if (!empty($count)) {
            // 对ID和订单号加密，拼装url路径
            $querydata = [
                'product_id' => $post['product_id'],
                'goods_id' => $post['goods_id'],
                'product_num' => $post['product_num']
            ];

            //存在规格则执行
            if (!empty($post['spec_value_id']) && 'undefined' != $post['spec_value_id']) {
                $querydata['spec_value_id'] = $post['spec_value_id'];
            }

            // 先 json_encode 后 md5 加密信息
            $querystr = md5(json_encode($querydata));

            // 存入缓存，一天有效期
            Cache::set($querystr, $querydata, 86400);

            // 跳转链接
            if (1 == input('param.fenbao/d')) {
                $url = '?querystr=' . $querystr;
            } else {
                $url = '/pages/seckill/product_buy/index?querystr=' . $querystr;
            }
            $this->success('数据正确', $url, ['querystr'=>$querystr, 'url'=>$url]);
        } else {
            $this->error('该商品不存在或已下架！');
        }
    }

    // 获取商品信息进行展示
    public function GetProductData($querystr = null, $usersData = [])
    {
        if (empty($usersData['users_id'])) $this->error('请先登录');
        $users_id = $usersData['users_id'];

        if (1 === intval($users_id)) {
            // 接收参数
            $post = input('post.');

            // 获取提交购买的商品数据
            [$list, $globalConfig] = model('ShopGoodsHandle')->getBuyGoodsListComment($post, $usersData, $this->usersConfig);

            // 获取会员地址信息
            [$address, $globalConfig] = model('ShopUsersHandle')->getUsersAddress($post, $usersData, $globalConfig);
        } else {
            /* 商品信息 */
            $CacheData = Cache::get($querystr);

            // 是否存在购买商品
            $list = $this->GetBuyOrderList($CacheData, $users_id);
            if (empty($list)) $this->error('链接失效，请重新下单');

            // 获取商城配置信息
            $ConfigData = getUsersConfigData('shop');

            /* 商品规格/折扣数据处理 */
            $list = $this->OrderProductProcess($list, $ConfigData);

            if (empty($list)) $this->error('商品库存不足或已过期！');
            /* END */

            /* 全局信息组装 */
            $GlobalInfo = $this->GetGlobalInfo($ConfigData, $CacheData);
            /* END */

            /* 商品数据整合处理 */
            foreach ($list as $key => $value) {
                /* 合计金额、合计数量计算 */
                if ($value['users_price'] >= 0 && !empty($value['product_num'])) {
                    // 计算单品小计
                    $list[$key]['subtotal'] = unifyPriceHandle($value['users_price'] * $value['product_num']);
                    // 计算合计金额
                    $GlobalInfo['TotalAmount'] += $list[$key]['subtotal'];
                    $GlobalInfo['TotalAmount'] = unifyPriceHandle($GlobalInfo['TotalAmount']);
                    // 计算合计数量
                    $GlobalInfo['TotalNumber'] += $value['product_num'];
                    // 判断订单类型，目前逻辑：一个订单中，只要存在一个普通商品(实物商品，需要发货物流)，则为普通订单
                    // 0表示为普通订单，1表示为虚拟订单，虚拟订单无需发货物流，无需选择收货地址，无需计算运费
                    if (empty($value['prom_type'])) $GlobalInfo['PromType'] = 0;
                }
                /* END */

                /* 规格处理 */
                $list[$key]['product_spec'] = '';
                if (!empty($value['spec_value_id'])) {
                    $spec_value_id = explode('_', $value['spec_value_id']);
                    if (!empty($spec_value_id)) {
                        $SpecWhere = [
                            'aid' => $value['product_id'],
                            'spec_value_id' => ['IN', $spec_value_id]
                        ];
                        $ProductSpecData = Db::name("product_spec_data")->where($SpecWhere)->field('spec_name, spec_value')->select();
                        foreach ($ProductSpecData as $spec_value) {
                            $list[$key]['product_spec'] .= $spec_value['spec_value'] . '；';
                        }
                    }
                }
                /* END */

                /* 规格处理 */
                $list[$key]['product_spec'] = '';
                if (!empty($value['spec_value_id'])) {
                    $spec_value_id = explode('_', $value['spec_value_id']);
                    if (!empty($spec_value_id)) {
                        $SpecWhere       = [
                            'aid'           => $value['product_id'],
                            'spec_value_id' => ['IN', $spec_value_id]
                        ];
                        $ProductSpecData = Db::name("product_spec_data")->where($SpecWhere)->field('spec_name, spec_value')->select();
                        foreach ($ProductSpecData as $spec_value) {
                            $list[$key]['product_spec'] .= $spec_value['spec_value'] . '；';
                        }
                    }
                }
                /* END */

                // 仅到店核销
                if ('2' === $value['logistics_type']) $GlobalInfo['onlyVerify'] = true;
                // 仅物流配送
                if ('1' === $value['logistics_type']) $GlobalInfo['onlyDelivery'] = true;
                // 物流配送 和 到店核销 都支持
                if ('1,2' === $value['logistics_type']) $GlobalInfo['allLogisticsType'] = true;
            }
            /* END */
            /* 默认地址 */
            $address = Db::name('shop_address')->where('users_id', $users_id)->order('is_default desc')->find();
            if (!empty($address)) {
                $address['region']['province'] = $this->GetRegionName($address['province']);
                $address['region']['city'] = $this->GetRegionName($address['city']);
                $address['region']['district'] = $this->GetRegionName($address['district']);
                $address['region']['detail'] = $address['address'];
                $address['address_all'] = $address['region']['province'] . $address['region']['city'] . $address['region']['district'] . $address['region']['detail'];

                if (!empty($GlobalInfo['shop_open_shipping'])) {
                    /* 查询运费 */
                    $where['province_id'] = $address['province'];
                    $template_money = Db::name('shop_shipping_template')->where($where)->getField('template_money');
                    if (0 == $template_money) {
                        $where['province_id'] = 100000;
                        $template_money = Db::name('shop_shipping_template')->where($where)->getField('template_money');
                    }
                    $address['template_money'] = $template_money;
                    /* END */
                } else {
                    $address['template_money'] = 0;
                }
            } else {
                $address['template_money'] = 0;
            }
            /* END */
            $verifyShow = input('verifyShow/d');
            if (!empty($verifyShow)) $address['template_money'] = 0;

            // 最终支付总金额
            if (!empty($post['deliveryShow']) && 1 === intval($post['deliveryShow'])) {
                // 订单总金额 + 运费金额
                $GlobalInfo['PayTotalAmount'] = unifyPriceHandle($GlobalInfo['TotalAmount'] + $address['template_money']);
            } else {
                $GlobalInfo['PayTotalAmount'] = unifyPriceHandle($GlobalInfo['TotalAmount']);
            }
        }

        // 数据返回
        $resultData = [
            // 个人信息
            'users' => $usersData,
            // 地址信息
            'address' => $address,
            // 全局信息
            'global_info' => !empty($globalConfig) ? $globalConfig : $GlobalInfo,
            // 商品信息
            'product_data' => $list
        ];

        $this->success('数据正确', null, $resultData);
    }

    // 获取下单的商品数据
    private function GetBuyOrderList($CacheData = [], $users_id = null)
    {
        $list = [];
        //判断是否还在秒杀时间内
        $seckill_arc = Db::name('weapp_seckill_archives')->where('goods_id',$CacheData['goods_id'])->find();
        if (empty($seckill_arc)) $this->error('秒杀商品不存在!');
        $act_id = $seckill_arc['act_id'];

        $seckill = Db::name('weapp_seckill')->where('id',$act_id)->find();
        if (empty($seckill)) $this->error('秒杀活动不存在!');
        if (empty($seckill['in_progress'])) $this->error('秒杀活动未开启!');
        if (1 !== $seckill['status']) $this->error('秒杀活动未开启!');
        if (getTime() > $seckill['end_time']) $this->error('秒杀活动已结束!');

        // 立即购买下单
        $where['a.aid'] = $CacheData['product_id'];
        if (!empty($CacheData['spec_value_id'])){
            $where['b.goods_id'] = $CacheData['goods_id'];
            //多规格
            $where['b.spec_value_id'] = $CacheData['spec_value_id'];
            $field = 'a.aid, a.typeid, a.title, a.litpic, a.users_price, a.stock_count, a.prom_type, a.attrlist_id, a.logistics_type, b.value_id, b.aid as product_id, b.spec_value_id,b.seckill_price, b.seckill_stock';
dump($where);
            $list = Db::name('archives')->alias('a')->field($field)
                ->join('weapp_seckill_product_spec_value b', 'a.aid = b.aid', 'LEFT')
                ->where($where)
                ->find();
            $spec_stock = Db::name('product_spec_value')->where(['aid'=>$CacheData['product_id'],'spec_value_id'=>$CacheData['spec_value_id']])->value('spec_stock');
            //秒杀库存不能大于商品库存
            if ($list['seckill_stock'] > $spec_stock) $list['seckill_stock'] = $spec_stock;
        }else{
            //单规格
            $list = Db::name('archives')->alias('a')->field('a.aid, a.typeid, a.title, a.litpic, a.users_price, a.stock_count, a.prom_type, a.attrlist_id, a.logistics_type')
                ->where($where)
                ->find();
            $list['product_id'] = $seckill_arc['aid'];
        }
dump($list);
exit;
        $list['goods_id'] = (int)$CacheData['goods_id'];
        $list['product_num'] = (int)$CacheData['product_num'];

        //判断该秒杀商品是多规格还是单规格
        if (empty($seckill_arc['is_spec'])) {
            //单规格
            $list['users_price'] = $list['seckill_price'] = $seckill_arc['seckill_price'];
            //秒杀库存不能大于商品库存
            if ($seckill_arc['seckill_stock'] > $list['stock_count']){
                $list['seckill_stock'] = $seckill_arc['seckill_stock'] = $list['stock_count'];
            }else{
                $list['stock_count'] = $list['seckill_stock'] = $seckill_arc['seckill_stock'];
            }
        }

        return $list;
    }

    // 商品规格/折扣数据处理
    private function OrderProductProcess($arc_data = [], $ConfigData = [])
    {
        $list[] = $arc_data;
        foreach ($list as $key => $value) {
            // 规格处理
            $list[$key]['product_spec_list'] = !empty($value['spec_value_id']) ? model('ShopPublicHandle')->getGoodsSpecList($value) : [];

            /* 未开启多规格则执行 */
            if (!isset($ConfigData['shop_open_spec']) || empty($ConfigData['shop_open_spec'])) {
                $value['spec_value_id'] = $value['seckill_price'] = $value['seckill_stock'] = 0;
                $list[$key]['spec_value_id'] = $list[$key]['seckill_price'] = $list[$key]['seckill_stock'] = 0;
            }
            /* END */

            /* 商品存在规格且价格不为空，则覆盖商品原来的价格 */
            if (!empty($value['spec_value_id']) && 'undefined' != $value['spec_value_id'] && $value['seckill_price'] >= 0) {
                // 规格价格覆盖商品原价
                $list[$key]['users_price'] = $value['users_price'] = $value['seckill_price'];
            }

            // 商品原价
            $list[$key]['old_price'] = unifyPriceHandle($list[$key]['users_price']);

            /* 商品存在规格且库存不为空，则覆盖商品原来的库存 */
            if (!empty($value['seckill_stock'])) {
                // 规格库存覆盖商品库存
                $list[$key]['stock_count'] = $value['stock_count'] = $value['seckill_stock'];
            }
            if ($value['product_num'] > $value['stock_count']) {
                // 购买数量超过最大库存量则默认购买最大库存量
                $list[$key]['stock_count'] = $list[$key]['product_num'] = $value['stock_count'];
            }
            /* END */

            // 图片处理
            $list[$key]['litpic'] = $this->get_default_pic($value['litpic'], true);

            /* 若库存为空则清除这条数据 */
            if (empty($value['stock_count'])) {
                unset($list[$key]);
                continue;
            }
            /* END */
        }

        return $list;
    }

    // 组装订单流程所需全局信息
    private function GetGlobalInfo($ConfigData = [], $CacheData = [])
    {
        $GlobalInfo = [
            // 温馨提示内容,为空则不展示
            'shop_prompt' => !empty($ConfigData['shop_prompt']) ? $ConfigData['shop_prompt'] : '',
            // 是否开启线下支付(货到付款)
            'shop_open_offline' => !empty($ConfigData['shop_open_offline']) ? $ConfigData['shop_open_offline'] : 0,
            // 是否开启运费设置
            'shop_open_shipping' => !empty($ConfigData['shop_open_shipping']) ? $ConfigData['shop_open_shipping'] : 0,
            // 初始化支付总额
            'TotalAmount' => 0,
            // 初始化支付总额，包含运费
            'PayTotalAmount' => 0,
            // 初始化总数
            'TotalNumber' => 0,
            // 提交来源:0购物车;1直接下单
            'submit_order_type' => !empty($CacheData) ? 1 : 0,
            // 1表示为虚拟订单
            'PromType' => 1,
            // 虚拟订单文案
            'virtual_text1' => '1、该产品为虚拟产品，仅支持在线支付且无需选择收货地址及运费计算。',
            'virtual_text2' => '2、若产品是充值类产品，请将您的手机号或需充值的卡号填入留言中。',
            // 仅到店核销
            'onlyVerify'         => false,
            // 仅物流配送
            'onlyDelivery'       => false,
            // 物流支持
            'allLogisticsType'   => false,
        ];

        return $GlobalInfo;
    }

    // 订单结算
    public function ShopOrderPay($post = [], $usersData = [])
    {
        // 订单提交时基础验证
        model('ShopOrderHandle')->orderSubmitBasicVerify($post, $this->usersConfig);

        // 获取提交购买的商品数据
        [$list, $globalConfig] = model('ShopGoodsHandle')->getBuyGoodsListComment($post, $usersData, $this->usersConfig);

        $goodsTitle = [];
        // 商品数据整合处理 
        foreach ($list as $key => $value) {
            $goodsTitle[] = !empty($value['title']) ? trim($value['title']) : '';
        }
        $goodsTitle = !empty($goodsTitle) ? implode('、', $goodsTitle) : '';

        // 获取会员地址信息
        [$address, $globalConfig] = model('ShopUsersHandle')->getUsersAddress($post, $usersData, $globalConfig);

        // 创建订单信息
        $orderData = model('ShopOrderHandle')->createOrderData($post, $address, $usersData, $globalConfig, $this->usersConfig);

        // 订单提交处理 -- 其他逻辑公共调用方法，部分逻辑改动不适合直接修改原文件时请在此方法做处理和兼容
        $orderData = model('ShopPublicHandle')->orderSubmitPublicHandle($orderData, $this->usersConfig, $this->users_id, $post, $list);

        // 添加订单信息
        $orderID = Db::name('shop_order')->insertGetId($orderData);
        if (!empty($orderID)) {
            // 订单ID
            $orderData['order_id'] = $orderID;

            // 订单创建后续处理 -- 其他逻辑公共调用方法，部分逻辑改动不适合直接修改原文件时请在此方法做处理和兼容
            $orderData = model('ShopPublicHandle')->orderCreatePublicHandle($orderData, $this->usersConfig, $this->users_id, $post, $list);

            // 创建订单商品信息
            [$cartIds, $orderGoods, $upSpecValue] = model('ShopOrderHandle')->createOrderGoodsData($post, $list, $usersData, $orderData, $globalConfig);

            // 添加订单商品数据
            $detailsID = Db::name('shop_order_details')->insertAll($orderGoods);
            if (!empty($orderID) && !empty($detailsID)) {
                // 订单创建添加成功后续处理
                model('ShopOrderHandle')->createOrderHandle($cartIds, $orderData, $upSpecValue);

                // 获取商品库存预警邮箱推送ID组
                $stockWarningEmail = model('ShopPublicHandle')->goodsStockWarningSendContent($usersData, $list, $orderID, $this->usersConfig['goods_stock_warning']);
                // 获取商品库存预警邮箱推送ID组
                $stockWarningMobile = model('ShopPublicHandle')->goodsStockWarningSendContent($usersData, $list, $orderID, $this->usersConfig['goods_stock_warning'], 'mobile');

                // 返回订单选择的支付渠道
                model('ShopOrderPayHandle')->returnOrderPayChannel($post, $usersData, $orderData, $this->usersConfig, $stockWarningEmail, $stockWarningMobile);
            }
            // 订单详情添加失败则删除已添加的订单主表数据
            else if (!empty($orderID) && empty($detailsID)) {
                Db::name('shop_order')->where('order_id', $orderID)->delete(true);
                $this->error('订单生成失败，商品数据有误！');
            }
        }

        $this->error('订单生成失败，商品数据有误！');
    }

    // 微信小程序支付后续处理
    public function WechatAppletsPayDealWith($PostData = [], $notify = false)
    {
        $OpenID = !empty($PostData['openid']) ? $PostData['openid'] : '';
        $UsersID = !empty($PostData['users_id']) ? $PostData['users_id'] : '';
        $OrderID = $PostData['order_id'];
        $OrderCode = $PostData['order_code'];

        if (!empty($OrderID) && !empty($OrderCode)) {
            /* 查询本站订单状态 */
            $where = [
                'users_id' => $UsersID,
                'order_id' => $OrderID,
                'order_code' => $OrderCode,
            ];
            $order = Db::name('shop_order')->where($where)->find();

            if (empty($order)) {
                $this->error('无效订单！');
            } else if (0 < $order['order_status']) {
                // 订单已支付
                if ($notify === true) { // 异步
                    return [
                        'code' => 1,
                        'msg' => 'ok',
                    ];
                } else { // 同步
                    $usersData = Db::name('users')->where('users_id', $UsersID)->find();
                    // 邮箱发送
                    $resultData['email'] = GetEamilSendData(tpCache('smtp'), $usersData, $order, 1, 'wechat');
                    // 短信发送
                    $resultData['mobile'] = GetMobileSendData(tpCache('sms'), $usersData, $order, 1, 'wechat');
                    $this->success('支付完成', '/pages/order/index', $resultData);
                }
            }

            // 当前时间戳
            $time = getTime();

            // 当前时间戳 + OpenID 经 MD5加密
            $nonceStr = md5($time . $OpenID);

            // 调用支付接口参数
            $params = [
                'appid' => $this->miniproInfo['appid'],
                'mch_id' => $this->miniproInfo['mchid'],
                'nonce_str' => $nonceStr,
                'out_trade_no' => $OrderCode
            ];

            // 生成参数签名
            $params['sign'] = $this->ParamsSign($params, $this->miniproInfo['apikey']);

            // 生成参数XML格式
            $ParamsXml = $this->ParamsXml($params);

            // 调用接口返回数据
            $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
            $result = $this->HttpsPost($url, $ParamsXml);

            // 解析XML格式
            $ResultData = $this->ResultXml($result);

            // 订单是否存在
            if (!empty($ResultData) && 'SUCCESS' == $ResultData['return_code'] && 'OK' == $ResultData['return_msg']) {
                // if ('NOTPAY' == $ResultData['trade_state'] && !empty($ResultData['trade_state_desc'])) {
                //     // 订单未支付
                //     $this->error($ResultData['trade_state_desc']);
                // }
                if ('SUCCESS' == $ResultData['trade_state']) {
                    // 订单已支付，处理订单流程
                    $where = [
                        'order_id' => $OrderID,
                        'users_id' => $order['users_id']
                    ];
                    $update = [
                        'order_status' => 1,
                        'pay_details' => serialize($ResultData),
                        'pay_time' => getTime(),
                        'update_time' => getTime()
                    ];
                    $ResultID = Db::name('shop_order')->where($where)->update($update);
                    if (!empty($ResultID)) {
                        // 添加订单操作记录
                        AddOrderAction($OrderID, $order['users_id'], 0, 1, 0, 1, '支付成功！', '会员使用微信小程序完成支付！');

                        // 保存微信发货推送表记录
                        if ('wechat' == $order['pay_name']) {
                            model('ShopPublicHandle')->saveWxShippingInfo($order['users_id'], $order['order_code'], 2);
                            if (2 == $order['logistics_type']){
                                //核销商品 推送微信发货
                                $WxPayOrderLogic = new \app\common\logic\WxPayOrderLogic();
                                $WxPayOrderLogic->minipro_send_goods($order,2);
                            }
                        }

                        // 虚拟自动发货
                        $ShopModel = new \app\user\model\Pay();
                        $ShopModel->afterVirtualProductPay($order);

                        // 订单支付通知
                        $params = [
                            'users_id' => $UsersID,
                            'result_id' => $OrderID,
                        ];
                        eyou_send_notice(9, $params);

                        // 站内信通知
                        $order['pay_method'] = '微信支付';
                        SendNotifyMessage($order, 5, 1, 0);

                        // 订单支付完成
                        if ($notify === true) { // 异步
                            return [
                                'code' => 1,
                                'msg' => 'ok',
                            ];
                        } else {// 同步
                            // 统计销售额
                            eyou_statistics_data(2);
                            eyou_statistics_data(3, $order['order_amount']);

                            $usersData = Db::name('users')->where('users_id', $UsersID)->find();
                            // 邮箱发送
                            $resultData['email'] = GetEamilSendData(tpCache('smtp'), $usersData, $order, 1, 'wechat');
                            // 短信发送
                            $resultData['mobile'] = GetMobileSendData(tpCache('sms'), $usersData, $order, 1, 'wechat');
                            $this->success('支付完成', '/pages/order/index', $resultData);
                        }
                    }
                }
            }
        }
    }


    // 微信小程序支付
    private function GetWechatAppletsPay($OpenID = null, $out_trade_no = null, $total_fee = null, $notify_url = '')
    {
        $notify_url = !empty($notify_url) ? $notify_url : url('api/v1.Api/wxpay_notify', [], true, true, 1, 1);
        // 当前时间戳
        $time = time();

        // 当前时间戳 + OpenID 经 MD5加密
        $nonceStr = md5($time . $OpenID);

        // 调用支付接口参数
        $params = [
            'appid' => $this->miniproInfo['appid'],
            'attach' => "微信小程序支付",
            'body' => "商品支付",
            'mch_id' => $this->miniproInfo['mchid'],
            'nonce_str' => $nonceStr,
            'notify_url' => $notify_url,
            'openid' => $OpenID,
            'out_trade_no' => $out_trade_no,
            'spbill_create_ip' => $this->GetClientIP(),
            'total_fee' => strval($total_fee * 100),
            'trade_type' => 'JSAPI'
        ];

        // 生成参数签名
        $params['sign'] = $this->ParamsSign($params, $this->miniproInfo['apikey']);

        // 生成参数XML格式
        $ParamsXml = $this->ParamsXml($params);

        // 调用接口返回数据
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = $this->HttpsPost($url, $ParamsXml);

        // 解析XML格式
        $ResultData = $this->ResultXml($result);

        // 数据返回
        if ($ResultData['return_code'] == 'SUCCESS' && $ResultData['return_msg'] == 'OK') {
            // 返回支付所需参数
            $ReturnData = [
                'prepay_id' => $ResultData['prepay_id'],
                'nonceStr' => $nonceStr,
                'timeStamp' => strval($time),
                'return_code' => $ResultData['return_code']
            ];
            // 生成支付所需的签名
            $ReturnData['paySign'] = $this->PaySign($this->miniproInfo['appid'], $params['nonce_str'], $ResultData['prepay_id'], $time);
            return $ReturnData;
        } else if ($ResultData['return_code'] == 'FAIL') {
            if ($ResultData['return_msg'] == '签名错误') {
                $ResultData['return_msg'] = '小程序支付配置不正确！';
            }
            return $ResultData;
        } else {
            return $ResultData;
        }
    }

    private function ParamsSign($values, $apikey)
    {
        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->ParamsUrl($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $apikey;
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    private function ParamsUrl($values)
    {
        $Url = '';
        foreach ($values as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $Url .= $k . '=' . $v . '&';
            }
        }
        return trim($Url, '&');
    }

    private function ParamsXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    private function ResultXml($xml)
    {
        // 禁止引用外部xml实体
        @libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    private function GetClientIP()
    {
        if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
            $ip = getenv('REMOTE_ADDR');
        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return preg_match('/[\d\.]{7,15}/', $ip, $matches) ? $matches [0] : '';
    }

    private function HttpsPost($url, $data)
    {
        $result = httpRequest($url, 'POST', $data);
        return $result;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        // curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 );
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            return 'Errno: ' . curl_error($ch);
        }
        curl_close($ch);
        return $result;
    }

    private function PaySign($appid, $nonceStr, $prepay_id, $timeStamp)
    {
        $data = [
            'appId' => $appid,
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
            'timeStamp' => $timeStamp,
        ];
        // 签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->ParamsUrl($data);
        // 签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->miniproInfo['apikey'];
        // 签名步骤三：MD5加密
        $string = md5($string);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    // 扣除商品库存 既要扣秒杀的也要扣原商品的,库存同步
    private function ProductStockProcessing($SpecValue = [])
    {
        if (!empty($SpecValue)) {
            foreach ($SpecValue as $key => $value) {
                if (!empty($value['value_id'])) {
                    //秒杀订单
                    $SpecUpData = [
                        'seckill_stock' => Db::raw('seckill_stock-' . ($value['quantity'])),
                        'seckill_sales_num' => Db::raw('seckill_sales_num+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];
                    Db::name('weapp_seckill_product_spec_value')->where('value_id', $value['value_id'])->update($SpecUpData);
                    $ArcUpData = [
                        'buy_count' => Db::raw('buy_count+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];
                    Db::name('weapp_seckill_archives')->where('goods_id', $value['goods_id'])->update($ArcUpData);

                    $oriSpecUpData = [
                        'spec_stock'     => Db::raw('spec_stock-' . ($value['quantity'])),
                        'spec_sales_num' => Db::raw('spec_sales_num+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];
                    Db::name('product_spec_value')->where(['aid'=> $value['aid'],'spec_value_id'=> $value['spec_value_id']])->update($oriSpecUpData);

                    $oriArcUpData = [
                        'stock_count' => Db::raw('stock_count-' . ($value['quantity'])),
                        'sales_num'   => Db::raw('sales_num+' . ($value['quantity'])),
                        'sales_all'   => Db::raw('sales_all+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];

                } else {
                    $ArcUpData = [
                        'stock' => Db::raw('stock-' . ($value['quantity'])),
                        'buy_count' => Db::raw('buy_count+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];
                    $oriArcUpData = [
                        'stock_count' => Db::raw('stock_count-' . ($value['quantity'])),
                        'sales_num'   => Db::raw('sales_num+' . ($value['quantity'])),
                        'sales_all'   => Db::raw('sales_all+' . ($value['quantity'])),
                        'update_time' => getTime(),
                    ];
                }
                Db::name('weapp_seckill_archives')->where('goods_id', $value['goods_id'])->update($ArcUpData);
                Db::name('archives')->where('aid', $value['aid'])->update($oriArcUpData);
            }
        }
    }

    // 获取用户下单时提交的收货地址及运费
    private function GetUsersAddrData($post = [], $PromType = 0)
    {
        $AddrData = [];
        if (0 == $PromType) {
            // 查询收货地址
            $AddrWhere   = [
                'addr_id'  => $post['addr_id'],
                'users_id' => $post['users_id'],
            ];
            $AddressData = Db::name('shop_address')->where($AddrWhere)->find();
            if (empty($AddressData)) $this->error('收货地址不存在！');

            $shop_open_shipping = getUsersConfigData('shop.shop_open_shipping');
            $template_money     = '0.00';
            if (!empty($shop_open_shipping)) {
                // 通过省份获取运费模板中的运费价格
                $where['province_id'] = $AddressData['province'];
                $template_money       = Db::name('shop_shipping_template')->where($where)->getField('template_money');
                if (0 == $template_money) {
                    // 省份运费价格为0时，使用统一的运费价格，固定ID为100000
                    $where['province_id'] = 100000;
                    $template_money       = Db::name('shop_shipping_template')->where($where)->getField('template_money');
                }
            }
        }

        // 拼装数组
        $AddrData = [
            'consignee'    => !empty($AddressData['consignee']) ? $AddressData['consignee'] : '',
            'country'      => !empty($AddressData['country']) ? $AddressData['country'] : '',
            'province'     => !empty($AddressData['province']) ? $AddressData['province'] : '',
            'city'         => !empty($AddressData['city']) ? $AddressData['city'] : '',
            'district'     => !empty($AddressData['district']) ? $AddressData['district'] : '',
            'address'      => !empty($AddressData['address']) ? $AddressData['address'] : '',
            'mobile'       => !empty($AddressData['mobile']) ? $AddressData['mobile'] : '',
            'shipping_fee' => !empty($template_money) ? $template_money : 0,
        ];

        return $AddrData;
    }

    // 旧产品属性处理
    private function ProductAttrProcessing($value = array())
    {
        $attr_value = '';
        $AttrWhere = [
            'a.aid' => $value['aid'],
        ];
        $attrData = Db::name('product_attr')
            ->alias('a')
            ->field('a.attr_value as value, b.attr_name as name')
            ->join('__PRODUCT_ATTRIBUTE__ b', 'a.attr_id = b.attr_id', 'LEFT')
            ->where($AttrWhere)
            ->order('b.sort_order asc, a.attr_id asc')
            ->select();
        foreach ($attrData as $val) {
            $attr_value .= $val['name'] . '：' . $val['value'] . '<br/>';
        }
        return $attr_value;
    }

    // 新商品属性处理
    private function ProductNewAttrProcessing($value = array())
    {
        $attr_value = '';
        $where = [
            'a.list_id' => $value['attrlist_id'],
            'a.status' => 1,
            'b.aid' => $value['aid']
        ];
        $attrData = Db::name('shop_product_attribute')
            ->alias('a')
            ->field('a.attr_name as name, b.attr_value as value')
            ->join('__SHOP_PRODUCT_ATTR__ b', 'a.attr_id = b.attr_id', 'LEFT')
            ->where($where)
            ->order('a.sort_order asc, a.attr_id asc')
            ->select();
        foreach ($attrData as $val) {
            $attr_value .= $val['name'] . '：' . $val['value'] . '<br/>';
        }
        return $attr_value;
    }

    // 商品规格处理
    private function ProductSpecProcessing($value = array())
    {
        $spec_value_s = '';
        if (!empty($value['spec_value_id'])) {
            $spec_value_id = explode('_', $value['spec_value_id']);
            if (!empty($spec_value_id)) {
                $SpecWhere = [
                    'aid' => $value['aid'],
                    'spec_value_id' => ['IN', $spec_value_id],
                ];
                $ProductSpecData = Db::name("product_spec_data")->where($SpecWhere)->field('spec_name,spec_value')->select();
                foreach ($ProductSpecData as $spec_value) {
                    $spec_value_s .= $spec_value['spec_name'] . '：' . $spec_value['spec_value'] . '<br/>';
                }
            }
        }
        return $spec_value_s;
    }
    /*商城购物流程--END*/

    /**
     * 构建支付请求的参数
     * @param $users
     * @param $order
     * @param $payType
     * @return array
     * @throws BaseException
     * @throws \think\exception\DbException
     */
    public function onOrderPayment($users, $order, $payType = 20)
    {
        if ($payType == 20) { // 微信支付
            return $this->onPaymentByWechat($users, $order);
        }
        return [];
    }

    /**
     * 构建微信支付请求
     * @param $users
     * @param $order
     * @return array
     * @throws BaseException
     * @throws \think\exception\DbException
     */
    protected function onPaymentByWechat($users, $order)
    {
        // 统一下单API
        if (empty($this->miniproInfo['mchid']) || empty($this->miniproInfo['apikey'])) {
            return [
                'code' => 0,
                'msg' => '很抱歉，请完善微信支付 [设置]-[商城设置]-[支付设置]',
            ];
        }
        $openid = Db::name('weapp_diyminipro_mall_users')->where(['users_id' => $users['users_id']])->getField('openid');
        $payment = $this->unifiedorder($order['order_code'], $openid, $order['order_amount']);

        return $payment;
    }

    /**
     * 统一下单API
     * @param $order_code
     * @param $openid
     * @param $totalFee
     * @return array
     * @throws BaseException
     */
    public function unifiedorder($order_code, $openid, $totalFee)
    {
        // 当前时间
        $time = getTime();
        // 生成随机字符串
        $nonceStr = md5($time . $openid);

        // API参数
        $params = [
            'appid' => $this->miniproInfo['appid'],
            'attach' => '微信小程序支付',
            'body' => $order_code,
            'mch_id' => $this->miniproInfo['mchid'],
            'nonce_str' => $nonceStr,
            'notify_url' => url('api/v1/Api/wxpay_notify', [], true, true, 1, 1),  // 异步通知地址
            'openid' => $openid,
            'out_trade_no' => $order_code,
            'spbill_create_ip' => clientIP(),
            'total_fee' => $totalFee * 100, // 价格:单位分
            'trade_type' => 'JSAPI',
        ];
        // 生成签名
        $params['sign'] = model('v1.User')->makeSign($params, $this->miniproInfo['apikey']);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = httpRequest($url, 'POST', model('v1.User')->toXml($params));
        $prepay = model('v1.User')->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            return [
                'code' => 0,
                'msg' => "微信支付api：{$prepay['return_msg']}",
            ];
        }
        if ($prepay['result_code'] === 'FAIL') {
            return [
                'code' => 0,
                'msg' => "微信支付api：{$prepay['err_code_des']}",
            ];
        }
        // 生成 nonce_str 供前端使用
        $paySign = $this->makePaySign($this->miniproInfo['appid'], $params['nonce_str'], $prepay['prepay_id'], $time);
        return [
            'prepay_id' => $prepay['prepay_id'],
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$time,
            'paySign' => $paySign
        ];
    }

    /**
     * 生成paySign
     * @param $nonceStr
     * @param $prepay_id
     * @param $timeStamp
     * @return string
     */
    private function makePaySign($appid, $nonceStr, $prepay_id, $timeStamp)
    {
        $data = [
            'appId' => $appid,
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
            'timeStamp' => $timeStamp,
        ];
        // 签名步骤一：按字典序排序参数
        ksort($data);
        $string = model('v1.User')->toUrlParams($data);
        // 签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->miniproInfo['apikey'];
        // 签名步骤三：MD5加密
        $string = md5($string);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    // 地址ID 换取地址名称
    private function GetRegionName($id = 1)
    {
        $name = Db::name('region')->where('id', $id)->getField('name');
        return $name;
    }
}